﻿using Jinndev.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using Unity.Mathematics;
using UnityEngine;

namespace Jinndev.Modding {

    /// <summary>
    /// 主配置
    /// </summary>
    public class Config : IEquatable<Config>, IConfig, IConfigIcon {

        public static readonly int EmptyID = 0; // ModManager.configList的0号索引的Config.id = 1

        public static Config Create(Mod mod, ConfigEntry entry, string name, string file) {
            Config config = new Config();
            config.mod = mod;
            config.entry = entry;
            config.name = name;
            config.file = file;

            config.category = entry.category;
            config.sid = mod.name + "." + name;

            return config;
        }

        // 配置文件中的属性
        public string name;
        public string icon;

        public Mod mod; // 所属mod引用
        public ConfigEntry entry;
        public string file; // 文件路径，用于ModEditor
        public string suffix; // 文件名后缀，用于自定义逻辑

        public int category; // 分类，由ModManager设置
        public string sid; // 字符串型id，用于程序运行时以及存档，由ModManager设置
        public int id; // 整型id，用于程序运行时以及存档，由ModManager设置

        public IReadOnlyDictionary<Type, SubConfig> SubConfigs {
            get { return subConfigs; }
        }

        private readonly Dictionary<Type, SubConfig> subConfigs = new Dictionary<Type, SubConfig>();
        private Texture2D _icon;


        public void Init() {
            if (!string.IsNullOrWhiteSpace(icon)) {
                _icon = ResourceManager.Load<Texture2D>(mod.path, icon);
                if (_icon == null) {
                    Debug.LogError("Icon not found: " + Path.Combine(mod.path, icon));
                }
            }
        }

        public void InitSubConfigs(List<Type> subConfigTypeList) {
            foreach (Type subConfigType in subConfigTypeList) {
                try {
                    if (subConfigs.TryGetValue(subConfigType, out SubConfig subConfig)) {
                        subConfig.Init(this);
                    }
                    else if (SubConfig.IsAutoCreate(subConfigType, category)) {
                        // 支持自动创建
                        subConfig = Activator.CreateInstance(subConfigType) as SubConfig;
                        AddSubConfig(subConfig);
                        subConfig.Init(this);
                    }
                }
                catch (Exception e) {
                    Debug.LogException(e);
                }
            }
        }

        public void AddSubConfig(SubConfig subConfig) {
            if (!subConfigs.ContainsKey(subConfig.GetType())) {
                subConfigs.Add(subConfig.GetType(), subConfig);
            }
            else {
                Debug.LogError($"SubConfig '{subConfig.GetType()}' already exists");
            }
        }

        public bool RemoveSubConfig(Type type) {
            return subConfigs.Remove(type);
        }

        public T GetSubConfig<T>() where T : SubConfig {
            if (subConfigs.TryGetValue(typeof(T), out SubConfig config)) {
                return config as T;
            }
            return null;
        }

        public SubConfig GetSubConfig(Type type) {
            if (subConfigs.TryGetValue(type, out SubConfig config)) {
                return config;
            }
            return null;
        }

        public bool HasSubConfig<T>() where T : SubConfig {
            return subConfigs.ContainsKey(typeof(T));
        }

        public bool HasSubConfig(Type type) {
            return subConfigs.ContainsKey(type);
        }

        public Config Base { get { return this; } }

        public int Id { get { return id; } }

        public string Sid { get { return sid; } }

        public int Category { get { return category; } }

        public Mod Mod { get { return mod; } }

        public Icon GetIcon() {
            return new Icon(_icon);
        }

        public override bool Equals(object obj) {
            return obj is Config && this == (Config)obj;
        }

        public override int GetHashCode() {
            return id.GetHashCode();
        }

        public bool Equals(Config other) {
            return other != null && other.id == id && other.mod == mod;
        }

        /// <summary>
        /// 输出json
        /// </summary>
        public JObject ToJson() {
            JObject json = new JObject();

            // 主配置仅输出以下字段
            json["name"] = name;
            if (icon != null) {
                json["icon"] = icon;
            }

            foreach (var pair in subConfigs) {
                string name = ModUtil.GetSubConfigNodeName(pair.Key);
                json[name] = pair.Value.ToJson();
            }
            return json;
        }

        /// <summary>
        /// 输出json字符串
        /// </summary>
        public string ToJsonString(bool prettyPrint = true) {
            JObject json = ToJson();
            if (prettyPrint) {
                return JsonUtil.ToTabIndentString(json);
            }
            else {
                return JsonUtil.ToString(json, false);
            }
        }


        public class Icon {
            public Texture2D texture;
            public Rect rect;
            public float4 tilingOffset;
            public float2 offset;

            public Icon(Texture2D texture, Rect rect, float4 tilingOffset, float2 offset) {
                this.texture = texture;
                this.rect = rect;
                this.tilingOffset = tilingOffset;
                this.offset = offset;
            }

            public Icon(Texture2D texture) {
                this.texture = texture;
                rect = new Rect(0, 0, 1, 1);
                tilingOffset = new float4(1, 1, 0, 0);
                offset = new float2(0, 0);
            }

            public override string ToString() {
                string name = texture == null ? null : texture.name;
                return $"{name}";
            }
        }

    }

}
